package com.mmd.gateway.filter;

import com.alibaba.fastjson.JSONObject;
import com.mmd.core.enums.ResultEnum;
import com.mmd.core.pojo.vo.BaseVO;
import com.mmd.core.result.Result;
import com.mmd.gateway.utils.IPUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public class GlobalFilterHandle implements GlobalFilter, Ordered {

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    // 访问多少次被限制
    private final static Long requestCount = 40L;
    // 多少分钟后访问
    private final static Integer timeSeconds = 5;
    // 锁前缀
    private final static String flowLimit = "Flow-limit:";

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        log.info("请求进来了 :{}",request.getURI());
//        BaseVO baseVO = flowLimit(request);
//        if (!baseVO.getCode().equals(CodeConstant.SUCCESS)) {
//            return exchange.getResponse().writeWith(responseDataFlux(exchange,baseVO));
//        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        // 最先执行该filter
        // 因为该filter会执行一系列过滤操作
        return Ordered.HIGHEST_PRECEDENCE;
    }

    /**
     * 限流
     */
    public BaseVO flowLimit(ServerHttpRequest request)
    {
        String ip = IPUtils.getIp(request);
        ValueOperations<String, String> stringStringValueOperations = redisTemplate.opsForValue();
        if(!StringUtils.isEmpty(stringStringValueOperations.get(flowLimit+ip))){
            return Result.custom(ResultEnum.FAIL_REQUEST_LIMIT.getCode(),"访问频繁请与"+redisTemplate.getExpire(flowLimit+ip)+"秒后在访问");
        }
        // 这里建议使用lua语法执行 线程安全不会出现并发问题 如果使用当前方式会出现并发问题导致请求会多出N个
        Object value = stringStringValueOperations.get(ip);
        if(StringUtils.isEmpty(value)){
            // 5秒中内访问多少次被限制
            value = 0;
        } else {
            value = Long.parseLong(String.valueOf(value))+1;
        }
        Long expire = redisTemplate.getExpire(ip);
        stringStringValueOperations.set(ip,String.valueOf(value),(expire == null || expire <= 0) ? timeSeconds : expire, TimeUnit.SECONDS);
        if(Objects.requireNonNull(Long.valueOf(String.valueOf(value)),"未获取到ip-key").compareTo(requestCount) > -1){
            stringStringValueOperations.set(flowLimit+ip,ip,timeSeconds, TimeUnit.MINUTES);
        }
        return Result.success();
    }

    /**
     * 响应对象
     */
    public Flux<DataBuffer> responseDataFlux(ServerWebExchange exchange,BaseVO baseVO)
    {
        exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
        return Flux.just(exchange.getResponse()
                .bufferFactory()
                .wrap(JSONObject.toJSONString(baseVO).getBytes(StandardCharsets.UTF_8))
        );
    }
}
